﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using BLToolkit.Data;
using BLToolkit.DataAccess;
using BLToolkit.Data.DataProvider;
//using CRootGUI.Common.Helpers;

namespace Clipping.Utils
{
	public static class DataProviderName
	{
		public const string Access = "Access";
		public const string SQLite = "SQLite";
	}

	/// <summary>
	/// 静态类，根据数据库类型获取对应的DataProvider对象
	/// </summary>
	public class DBDataProviderFactory
	{
		static List<DataProviderBase> _dataProviders;

		static DBDataProviderFactory _dbConnectionFactory = new DBDataProviderFactory();

		static DBDataProviderFactory()
		{
			_dataProviders = new List<DataProviderBase>();
			AddDataProvider(new AccessDataProvider());
			AddDataProvider(new SQLiteDataProvider());
		}

		static void AddDataProvider(DataProviderBase dataProvider)
		{
			_dataProviders.Add(dataProvider);
		}

		public static DataProviderBase GetDataProvider(string providerName)
		{
			DataProviderBase provider = _dataProviders.FirstOrDefault(x => x.Name == providerName);
			if (provider == null)
				provider = _dataProviders.FirstOrDefault(x => x.Name == ProviderName.Access);

			return provider;
		}

		DBDataProviderFactory() { }
	}

	/// <summary>
	/// 数据库连接
	/// </summary>
	public partial class DBConnection : DbManager
	{
		/// <summary>
		/// 当前线程默认操作的数据库连接对象
		/// </summary>
		[ThreadStatic]
		public static DBConnection DefaultConnection;

		public DBConnection(string providerName, string connectionString, DBConnectionPool dbpool)
			: base(DBDataProviderFactory.GetDataProvider(providerName), connectionString)
		{
			ConnectionString = connectionString;
			DBConnectionPool = dbpool;
			ThreadId = -1;
		}

		public DBConnection(string providerName, string connectionString)
			: base(DBDataProviderFactory.GetDataProvider(providerName), connectionString)
		{
			ConnectionString = connectionString;
			ThreadId = -1;
		}

		~DBConnection()
		{
			if (DBConnection.DefaultConnection == this)
				Dispose();
		}

		/// <summary>
		/// 数据库连接字符串
		/// </summary>
		public string ConnectionString { get; internal set; }

		/// <summary>
		/// 线程ID
		/// 用于判断当前DBConnection对象是被哪个线程使用
		/// </summary>
		public int ThreadId { get; internal set; }

		/// <summary>
		/// 引用次数
		/// </summary>
		protected int RefCount { get; private set; }

		/// <summary>
		/// 是否空闲
		/// </summary>
		private bool _isFree = true;
		public bool IsFree
		{
			get
			{
				return _isFree;
			}
			internal set
			{
				_isFree = value;

				//如果空闲，则通知
				if (_isFree && DBConnectionPool != null)
				{
					DBConnectionPool.FreeEvent.Set();
				}
			}
		}

		/// <summary>
		/// DbManager对象
		/// </summary>
		public DbManager DbManager { get { return this; } }

		/// <summary>
		/// 连接池对象
		/// </summary>
		public DBConnectionPool DBConnectionPool { get; internal set; }

		/// <summary>
		/// 重新连接
		/// </summary>
		public bool ReConnect()
		{
			try
			{
				Close();
				Connection.Open();

				return Connection.State == ConnectionState.Open;
			}
			catch (Exception err)
			{
				//忽略错误，但是需要记录日志
				//LogHelper.LogErrMsg(err);
			}
			return false;
		}

		/// <summary>
		/// 关闭数据库连接
		/// </summary>
		public new void Close()
		{
			DBConnectionPool.SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);

			base.Close();
		}

		/// <summary>
		/// 添加引用
		/// </summary>
		public void AddRef()
		{
			RefCount++;

			System.Threading.Monitor.Enter(this); //使用中避免被关闭
		}

		/// <summary>
		/// 释放数据库连接对象
		/// </summary>
		public void Release()
		{
			Dispose();
		}



        public void Free()
        {
            lock (this)
            {
                --RefCount;

                System.Threading.Monitor.Exit(this); //使用中避免被关闭

                if (RefCount == 0)
                {
                    try
                    {
                        //回退事务，放弃所有修改
                        this.RollbackTransaction();
                    }
                    finally
                    {
                        //释放数据库连接对象到连接池中，供其他线程使用
                        ThreadId = -1;
                        IsFree = true;

                        //释放当前线程使用的数据库连接对象
                        if (DBConnection.DefaultConnection == this)
                            DBConnection.DefaultConnection = null;
                    }
                }

                else if (RefCount < 0)
                {
                    RefCount = 0;

                    System.Diagnostics.Trace.WriteLine("警告：数据库连接的RefCount < 0，将被释放！");

                    Close();
                }
            }
        }
		protected override void Dispose(bool disposing)
		{
            //lock (this)
            //{
            //    --RefCount;

            //    System.Threading.Monitor.Exit(this); //使用中避免被关闭

            //    if (RefCount == 0)
            //    {
            //        try
            //        {
            //            //回退事务，放弃所有修改
            //            this.RollbackTransaction();
            //        }
            //        finally
            //        {
            //            //释放数据库连接对象到连接池中，供其他线程使用
            //            ThreadId = -1;
            //            IsFree = true;

            //            //释放当前线程使用的数据库连接对象
            //            if (DBConnection.DefaultConnection == this)
            //                DBConnection.DefaultConnection = null;
            //        }
            //    }

            //    else if (RefCount < 0)
            //    {
            //        RefCount = 0;

            //        System.Diagnostics.Trace.WriteLine("警告：数据库连接的RefCount < 0，将被释放！");

            //        Close();
            //    }
            //}
		}

		/// <summary>
		/// 操作异常处理
		/// </summary>
		/// <param name="op"></param>
		/// <param name="ex"></param>
		protected override void OnOperationException(OperationType op, BLToolkit.Data.DataException ex)
		{
			//记录错误日志
			switch (op)
			{
				case OperationType.PrepareCommand:
					//LogHelper.LogErrMsg(string.Format("预编译SQL语句出错，错误：{0}，SQL语句：{1}", ex.Message, SelectCommand.CommandText));
					break;

				case OperationType.Fill:
				case OperationType.Read:
				case OperationType.ExecuteReader:
				case OperationType.ExecuteNonQuery:
					//LogHelper.LogErrMsg(string.Format("数据操作[{0}]出错，错误：{1}，SQL语句：{2}", op.ToString(), ex.Message, SelectCommand.CommandText));
					break;

				case OperationType.Update:
					//LogHelper.LogErrMsg(string.Format("数据操作[{0}]出错，错误：{1}，SQL语句：{2}", op.ToString(), ex.Message, UpdateCommand.CommandText));
					break;

				default:
					//LogHelper.LogErrMsg(string.Format("数据操作[{0}]出错，错误：{1}", op.ToString(), ex.Message));
					break;
			}

			//记录日志
			base.OnOperationException(op, ex);
		}
	}


	/// <summary>
	/// 数据库连接池
	/// </summary>
	public class DBConnectionPool : IDisposable
	{
		[DllImport("kernel32.dll")]
		public static extern int SetProcessWorkingSetSize(IntPtr proc, int min, int max);

		/// <summary>
		/// 连接池有新的空闲对象时间
		/// </summary>
		public AutoResetEvent FreeEvent { get; set; }

		/// <summary>
		/// DBAL日志对象
		/// </summary>
		private static Log4cb.Log4cbHelper DBALLog = null;
		private static int logRefCount = 0;

		static DBConnectionPool()
		{
			//对所有的SQL进行记录
			DBConnection.TraceSwitch = new TraceSwitch("DB", "DB执行语句追踪") { Level = System.Diagnostics.TraceLevel.Verbose };
			DBConnection.WriteTraceLine = (message, displayName) =>
			{
				if (DBALLog == null) return;

				message = message.Replace("\r\n", "\t");
				DBALLog.LogDebugMsg(message);
			};
		}

		public DBConnectionPool(string providerName, string connectionString, int maxConnNum)
		{
			DataProviderName = providerName;
			ConnectionString = connectionString;
			MaxConnNum = maxConnNum <= 0 ? 1 : maxConnNum;
			Connections = new List<DBConnection>(MaxConnNum);

			FreeEvent = new AutoResetEvent(false);

			//创建日志对象
			logRefCount++;
			if (DBALLog == null)
				DBALLog = new Log4cb.Log4cbHelper("DBAL", Log4cb.BuiltInModules.DBAL);
		}

		~DBConnectionPool()
		{
			Dispose();
		}

		public void Dispose()
		{
			ClearAllConnection();

			if (logRefCount > 0)
			{
				logRefCount--;

				//释放日志对象
				if (logRefCount == 0 && DBALLog != null)
				{
					DBALLog.Dispose();
					DBALLog = null;
				}
			}
		}

		/// <summary>
		/// 数据库连接字符串
		/// </summary>
		public string ConnectionString { get; internal set; }

		/// <summary>
		/// 数据库类型
		/// </summary>
		public string DataProviderName { get; internal set; }

		/// <summary>
		/// 连接池允许的最大连接数
		/// </summary>
		public int MaxConnNum { get; internal set; }

		/// <summary>
		/// 连接池当前所有的连接
		/// </summary>
		internal List<DBConnection> Connections { get; set; }

		/// <summary>
		/// 获取一个空闲的数据库连接对象
		/// </summary>
		/// <returns></returns>
		public DBConnection GetConnection()
		{
			//默认超时时间是20s
			return GetConnection(20000, true);
		}

		/// <summary>
		/// 获取一个空闲的数据库连接对象
		/// </summary>
		/// <param name="usedByCurrentThread">是否获取当前线程已经获取的连接对象</param>
		/// <returns></returns>
		public DBConnection GetConnection(bool usedByCurrentThread)
		{
			//默认超时时间是20s
			return GetConnection(20000, usedByCurrentThread);
		}

		/// <summary>
        /// 获取一个空闲的数据库连接对象
		/// </summary>
		/// <param name="timeout">超时时间</param>
		/// <returns></returns>
		public DBConnection GetConnection(long timeout)
		{
			//默认超时时间是20s
			return GetConnection(timeout, true);
		}

		/// <summary>
		/// 获取一个空闲的数据库连接对象
		/// </summary>
		/// <param name="timeout">超时时间，单位毫秒ms</param>
        /// <param name="usedByCurrentThread">是否获取当前线程已经获取的连接对象</param>
		/// <returns></returns>
		public DBConnection GetConnection(long timeout, bool usedByCurrentThread)
		{
			DateTime dtStart = DateTime.Now;
			DBConnection conn = null;

			//首先查看当前线程是否已经获取了一个数据库连接对象，如果有，则返回
			if (usedByCurrentThread)
			{
				try
				{
					//避免多个线程同时获取连接
					lock (Connections)
					{
						conn = Connections.FirstOrDefault(x => !x.IsFree && x.ThreadId == Thread.CurrentThread.ManagedThreadId);
						if (conn != null)
						{
							lock (conn)
							{
								conn.AddRef();
								conn.IsFree = false;
								conn.ThreadId = Thread.CurrentThread.ManagedThreadId;
							}

							//检查线程默认的数据库连接对象
							//Debug.Assert(DBConnection.DefaultConnection == conn);
						}
					}
				}
				catch (Exception err)
				{
					throw new Exception(string.Format("获取同线程正在使用的DBConnection出错，信息：{0}，堆栈: {1}", err.ToString(), err.StackTrace.ToString()), err);
				}
			}

			//当前连接是否一切都OK的
			bool bOk = conn != null && conn.Connection.State == ConnectionState.Open;

			//循环判断是否有空闲数据库连接，并能够连接到数据库，直至超时
			while (!bOk && (DateTime.Now - dtStart).TotalMilliseconds < timeout)
			{
				try
				{
					if (conn == null)
					{
						lock (Connections)
						{
							//查询是否有空闲的连接
							conn = Connections.FirstOrDefault(x => x.IsFree);

							//如果没有空闲连接，但连接池还未满，则新建连接
							if (conn == null && Connections.Count < MaxConnNum)
							{
								//新建连接
								conn = new DBConnection(DataProviderName, ConnectionString, this);

								//添加到连接池
								Connections.Add(conn);
							}

							//修改连接的空闲状态
							if (conn != null)
							{
								lock (conn)
								{
									conn.AddRef();
									conn.IsFree = false;
									conn.ThreadId = Thread.CurrentThread.ManagedThreadId;
								}
							}
						}
					}

					//如果没有可用的连接，则等待
					if (conn == null)
					{
						Thread.Sleep(5);
						//FreeEvent.WaitOne(100);
						continue;
					}

					//确保连接到数据库
					try
					{
						if (conn != null && (conn.Connection.State == ConnectionState.Open || conn.ReConnect()))
						{
							bOk = true;
							break;
						}
						else
						{
							//每次循环间都等待100ms
							Thread.Sleep(10);
						}
					}
					catch (Exception err)
					{
						//忽略错误，但是需要记录日志
						//LogHelper.LogErrMsg(err, "连接数据库失败，原因：{0}", err.Message);
					}
				}
				catch (Exception err)
				{
					//LogHelper.LogErrMsg(err, "获取空闲DBConnection或创建新DBConnection出错, 原因：{0}", err.Message);
					throw new Exception(string.Format("获取空闲DBConnection或创建新DBConnection出错，信息：{0}，堆栈: {1}", err.ToString(), err.StackTrace.ToString()), err);
				}
			}

			//更新当前连接对象的线程ID标识
			if (conn == null)
			{
				try
				{
					lock (Connections)
					{
						//将当前所有数据库连接的状态枚举出来
						string sErr = "没有获取到数据库连接！ 连接池详细信息：连接个数（" + Connections.Count.ToString() + "）";
						foreach (var cn in Connections)
						{
							sErr += "\r\n" + ((cn == null || cn.Connection == null) ? "NULL" :
								("IsFree:" + cn.IsFree.ToString()
								+ ", ThreadID:" + cn.ThreadId.ToString()
								+ ", State:" + cn.Connection.State.ToString()));
						}

						Debug.Assert(false, sErr);
					}
				}
				catch (Exception err)
				{
					//LogHelper.LogErrMsg(err, "未获取有效的DBConnection，打印ConnectionPool信息出错， 原因：{0}", err.Message);
					throw new Exception(string.Format("未获取有效的DBConnection，打印ConnectionPool信息出错，信息：{0}，堆栈: {1}", err.ToString(), err.StackTrace.ToString()), err);
				}
			}

			//设置默认连接对象
			if (usedByCurrentThread && conn != null)
				DBConnection.DefaultConnection = conn;

			return conn;
		}

		/// <summary>
		/// 清空连接池内所有的数据库连接
		/// </summary>
		internal void ClearAllConnection()
		{
			SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);

			//释放数据库连接对象
			foreach (DBConnection conn in Connections)
			{
				lock (conn)
				{
					conn.Close();
				}
			}

			Connections.Clear();
		}
	}


	/// <summary>
	/// SqlQuery的封装，主要使其能够切换DbManager对象（DBConnection对象）
	/// </summary>
	public class DBSqlQuery : SqlQuery
	{
		public DBSqlQuery(DbManager dbManager)
			: base(dbManager)
		{
		}

		public DBSqlQuery()
			: base(DBConnection.DefaultConnection)
		{
		}

		public void SetDbManager(DbManager dbManager)
		{
			SetDbManager(dbManager, false);
		}

		public new void SetDbManager(DbManager dbManager, bool dispose)
		{
			base.SetDbManager(dbManager, dispose);
		}
	}

	/// <summary>
	/// SqlQuery的封装，主要使其能够切换DbManager对象（DBConnection对象）
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public class DBSqlQuery<T> : SqlQuery<T>
	{
		public DBSqlQuery(DbManager dbManager)
			: base(dbManager)
		{
		}

		public DBSqlQuery()
			: base(DBConnection.DefaultConnection)
		{
		}

		public void SetDbManager(DbManager dbManager)
		{
			SetDbManager(dbManager, false);
		}

		public new void SetDbManager(DbManager dbManager, bool dispose)
		{
			base.SetDbManager(dbManager, dispose);
		}
	}

	/// <summary>
	/// 支持不同数据库使用不同Sql语句的SqlQuery封装
	/// </summary>
	public class QueryTextAttribute : SqlQueryAttribute
	{
		public QueryTextAttribute(string sqlText)
		{
			SqlText = sqlText;
			IsDynamic = true;
		}

		/// <summary>
		/// SQLite数据库语句
		/// </summary>
		public string SQLiteText { get; set; }

		public override string GetSqlText(DataAccessor accessor, DbManager dbManager)
		{
			switch (dbManager.DataProvider.Name)
			{
				case DataProviderName.Access:
					return SqlText;

				case DataProviderName.SQLite:
					return SQLiteText ?? SqlText;

				default:
					return SqlText;
			}

			throw new ApplicationException(string.Format("Unsupportted data provider '{0}'", dbManager.DataProvider.Name));
		}
	}
}
